
package Game;

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.ImageIcon;

import jmetest.renderer.TestSkybox;
import jmetest.terrain.TestTerrain;

import com.jme.app.BaseGame;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.input.InputHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.light.DirectionalLight;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.CameraNode;
import com.jme.scene.Node;
import com.jme.scene.Skybox;
import com.jme.scene.shape.Box;
import com.jme.scene.state.CullState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.system.JmeException;
import com.jme.util.TextureManager;
import com.jme.util.Timer;
import com.jme.util.export.binary.BinaryImporter;

import com.jmex.model.converters.MaxToJme;
import com.jmex.terrain.TerrainBlock;
import com.jmex.terrain.util.MidPointHeightMap;
import com.jmex.terrain.util.ProceduralTextureGenerator;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;

public class JMEGraphic extends BaseGame {
    private static final Logger logger = Logger.getLogger(JMEGraphic.class
            .getName());

    public static TerrainBlock tb;
    //mapsize = 1024*1024
    private Skybox skybox;
    private FPSPlayer player;
    protected InputHandler input;
    protected Timer timer;
    private static Camera cam;
    public Node scene;
    public ArrayList<FPSPlayer> players = new ArrayList<FPSPlayer>();

    private int width, height, depth, freq;
    private boolean fullscreen;

    public JMEGraphic(ArrayList<FPSPlayer> players) {
        this.players = players;
    }
    
    /*public static void main(String[] args) {
        JMEGraphic app = new JMEGraphic();
        // We will load our own "fantastic" Flag Rush logo. Yes, I'm an artist.
        app.setConfigShowMode(ConfigShowMode.NeverShow, JMEGraphic.class
                .getClassLoader().getResource(
                        "jmetest/data/images/FlagRush.png"));
        app.start();
    }*/



    protected void update(float interpolation) {
        timer.update();
        interpolation = timer.getTimePerFrame();
        
        input.update(interpolation);
        
        skybox.setLocalTranslation(cam.getLocation());

        if (KeyBindingManager.getKeyBindingManager().isValidCommand("exit")) {
            finished = true;
        }

        if(cam.getLocation().y < (tb.getHeight(cam.getLocation())+2)) {
            cam.getLocation().y = tb.getHeight(cam.getLocation()) + 2;
            cam.update();
        }

        float characterMinHeight = tb.getHeight(player
                .getLocalTranslation())+((BoundingBox)player.getWorldBound()).yExtent;
        if (!Float.isInfinite(characterMinHeight) && !Float.isNaN(characterMinHeight)) {
            player.getLocalTranslation().y = characterMinHeight;
        }
        scene.updateGeometricState(interpolation, true);
        tb.getHeight(2,3);
    }


    protected void render(float interpolation) {
        display.getRenderer().clearBuffers();
        display.getRenderer().draw(scene);
    }

    protected void initSystem() {
        width = settings.getWidth();
        height = settings.getHeight();
        depth = settings.getDepth();
        freq = settings.getFrequency();
        fullscreen = settings.isFullscreen();

        try {
            display = DisplaySystem.getDisplaySystem(settings.getRenderer());
            display.createWindow(width, height, depth, freq, false);

            cam = display.getRenderer().createCamera(settings.getWidth(), settings.getHeight());
        } catch (JmeException e) {
            logger.log(Level.SEVERE, "Could not create displaySystem", e);
            System.exit(1);
        }

        display.getRenderer().setBackgroundColor(ColorRGBA.black.clone());

        timer = Timer.getTimer();

        display.getRenderer().setCamera(cam);

        KeyBindingManager.getKeyBindingManager().set("exit",
                KeyInput.KEY_ESCAPE);
    }

    /**
     * initializes the scene
     *
     * @see com.jme.app.BaseGame#initGame()
     */
    protected void initGame() {
        display.setTitle("Populous");

        scene = new Node("Scene graph node");
        /** Create a ZBuffer to display pixels closest to the camera above farther ones.  */
        ZBufferState buf = display.getRenderer().createZBufferState();
        buf.setEnabled(true);
        buf.setFunction(ZBufferState.TestFunction.LessThanOrEqualTo);
        scene.setRenderState(buf);

        //Time for a little optimization. We don't need to render back face triangles, so lets
        //not. This will give us a performance boost for very little effort.
        CullState cs = display.getRenderer().createCullState();
        cs.setCullFace(CullState.Face.Back);
        scene.setRenderState(cs);

        //Add terrain to the scene
        buildTerrain();
        //Light the world
        buildLighting();
        //Add the skybox
        buildSkyBox();
        
        buildESB();
        //Build the player
        buildPlayer();

        for (int i = 0; i < players.size(); i++){
            scene.attachChild(players.get(i));
        }
        //buildCamera();
        
        buildInput();
        cam.update();
        //buildFog();

        
        scene.updateGeometricState(0.0f, true);
        scene.updateRenderState();
    }
    
    private void buildPlayer() {
        //box stand in
        Node model = null;
        try {
        MaxToJme C1 = new MaxToJme();
        ByteArrayOutputStream BO = new ByteArrayOutputStream();
        File file = new File("M4a1anPws4.3DS");
        URL maxFile = file.toURI().toURL();
        C1.convert(new BufferedInputStream(maxFile.openStream()),BO);
        model = (Node)BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));

        model.setModelBound(new BoundingBox());
        model.updateModelBound();
        //scale it to be MUCH smaller than it is originally
        model.setLocalScale(0.02f);
        } catch (IOException e) {
            e.printStackTrace();
        }

        player = new FPSPlayer("Player Node",model);
        player.setLocalTranslation(new Vector3f(1000,0, tb.getHeight(100,0)));
        player.attachChild(players.get(0));
        scene.attachChild(player);
        scene.updateGeometricState(0, true);

        //player.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
    }

    private void buildESB(){
        Node model = null;
        try {
        MaxToJme C1 = new MaxToJme();
        ByteArrayOutputStream BO = new ByteArrayOutputStream();
        File file = new File("ESB_black_white_2.3ds");
        URL maxFile = file.toURI().toURL();
        C1.convert(new BufferedInputStream(maxFile.openStream()),BO);
        model = (Node)BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));

        model.setModelBound(new BoundingBox());
        model.updateModelBound();
        model.setLocalTranslation(500, tb.getHeight(500,500), 500);
        //scale it to be MUCH smaller than it is originally
        model.setLocalScale(.05f);
        Quaternion rot = new Quaternion();
        rot.fromAngleAxis(-FastMath.HALF_PI, new Vector3f(1,0,0));
        model.setLocalRotation(rot);
        scene.attachChild(model);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * creates a light for the terrain.
     */
    private void buildLighting() {
        /** Set up a basic, default light. */
        DirectionalLight light = new DirectionalLight();
        light.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
        light.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
        light.setDirection(new Vector3f(1,-1,0));
        light.setEnabled(true);

          /** Attach the light to a lightState and the lightState to rootNode. */
        LightState lightState = display.getRenderer().createLightState();
        lightState.setEnabled(true);
        lightState.attach(light);
        scene.setRenderState(lightState);
    }

    /**
     * build the height map and terrain block.
     */
    private void buildTerrain() {


        MidPointHeightMap heightMap = new MidPointHeightMap(256, 0.5f);
        // Scale the data
        Vector3f terrainScale = new Vector3f(4, 0.0575f, 4);
        // create a terrainblock
        tb = new TerrainBlock("Terrain", heightMap.getSize(), terrainScale,
                heightMap.getHeightMap(), new Vector3f(0, 0, 0));

        tb.setModelBound(new BoundingBox());
        tb.updateModelBound();

        // generate a terrain texture with 2 textures
        ProceduralTextureGenerator pt = new ProceduralTextureGenerator(
                heightMap);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/grassb.png")), -128, 0, 128);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/dirt.jpg")), 0, 128, 255);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/highest.jpg")), 128, 255,
                384);
        pt.createTexture(32);

        // assign the texture to the terrain
        TextureState ts = display.getRenderer().createTextureState();
        Texture t1 = TextureManager.loadTexture(pt.getImageIcon().getImage(),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear, true);
        ts.setTexture(t1, 0);

        tb.setRenderState(ts);
        tb.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
        scene.attachChild(tb);


    }

    /**
     * buildSkyBox creates a new skybox object with all the proper textures. The
     * textures used are the standard skybox textures from all the tests.
     *
     */
    private void buildSkyBox() {
        skybox = new Skybox("skybox", 10, 10, 10);

        Texture north = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/north.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture south = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/south.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture east = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/east.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture west = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/west.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture up = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/top.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture down = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/bottom.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);

        skybox.setTexture(Skybox.Face.North, north);
        skybox.setTexture(Skybox.Face.West, west);
        skybox.setTexture(Skybox.Face.South, south);
        skybox.setTexture(Skybox.Face.East, east);
        skybox.setTexture(Skybox.Face.Up, up);
        skybox.setTexture(Skybox.Face.Down, down);
        skybox.preloadTextures();
        scene.attachChild(skybox);
    }

    private void buildInput() {
        cam.setFrustum(1.0f, 1000.0f, -0.55f, 0.55f,0.4125f, -0.4125f);
        Vector3f loc = new Vector3f(4.0f, 0.0f, 0.0f);
        Vector3f left = new Vector3f(0.0f, 0.0f, 1.0f);
        Vector3f up = new Vector3f(0.0f, 1.0f,0.0f);
        Vector3f dir = new Vector3f(-1.0f, 0f, 0.0f);
        cam.setFrame(loc, left, up, dir);

        display.getRenderer().setCamera(cam);
        cam.update();
        CameraNode camNode = new CameraNode("camera", cam);
        //camNode.setLocalTranslation(player.getLocalTranslation().x,player.getLocalTranslation().y, player.getLocalTranslation().z);
        //camNode.setLocalTranslation(new Vector3f(1,2.5f,-7));
        player.attachChild(camNode);
        input = new CustomFPSInputHandler(cam, 50, 1, player);
        
    }

    public Node getScene() {
        return scene;
    }
    
    public void setScene(Node scene){
        this.scene = scene;
    }



    /**
     * will be called if the resolution changes
     *
     * @see com.jme.app.BaseGame#reinit()
     */
    protected void reinit() {
        display.recreateWindow(width, height, depth, freq, fullscreen);
    }

    /**
     * close the window and also exit the program.
     */
    protected void quit() {
        super.quit();
        System.exit(0);
    }

    /**
     * clean up the textures.
     *
     * @see com.jme.app.BaseGame#cleanup()
     */
    protected void cleanup() {

    }


    public static float getHeight(float x,float y){
        return tb.getHeight(x,y);
    }
}